﻿using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using WalkingTec.Mvvm.Core;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using SelfService.Model.Models;
using WalkingTec.Mvvm.Core.Extensions;


namespace SelfService.DataAccess
{
    public class DataContext : FrameworkContext
    {
        public DbSet<FrameworkUser> FrameworkUsers { get; set; }
        public DbSet<SysConfig> SysConfig { get; set; }
        public DbSet<SysConfigGroup> SysConfigGroups { get; set; }
        public DbSet<SysClient> SysClients { get; set; }
        public DbSet<SysDevice> SysDevices { get; set; }
        public DbSet<SysDeviceGroup> SysDevicesGroups { get; set; }
        public DbSet<SysModule> SysModules { get; set; }
        public DbSet<SysOperator> SysOperators { get; set; }
        public DbSet<SysOperatorGroup> SysOperatorsGroups { get; set; }
        public DbSet<SysCardType> SysCardTypes { get; set; }
        public DbSet<SysPayType> SysPayTypes { get; set; }
        public DbSet<SysPatient> SysPatients { get; set; }
        public DbSet<SysPatientLog> SysPatientLoges { get; set; }
        public DbSet<SysClientLog> SysClientsLog { get; set; }
        public DbSet<SysPrescription> SysPrescriptions { get; set; }
        public DbSet<SysPay> SysPays { get; set; }
        public DbSet<SysTheme> SysThemes { get; set; }
        public DbSet<SysDoctor> SysDoctors { get; set; }
        public DbSet<SysRoom> SysRooms { get; set; }
        public DbSet<SysScreenQueue> SysScreenQueues { get; set; }
        public DbSet<SysTimeInterval> SysTimeIntervals { get; set; }
        public DbSet<SysClientTask> SysClientsTask { get; set; }
        public DbSet<SysMedicalProject> SysMedicalProjects { get; set; }
        public DbSet<SysPrescriptionProjects> SysPrescriptionProjects { get; set; }
        public DbSet<SysTask> SysTasks { get; set; }
        public DbSet<SysAppointment> SysAppointments { get; set; }

        public DataContext(CS cs) : base(cs) { }

        public DataContext(string cs, DBTypeEnum dbType) : base(cs, dbType) { }

        public DataContext(string cs, DBTypeEnum dbType, string version = null) : base(cs, dbType, version)
        {

        }
        public DataContext(DbContextOptions<DataContext> options) : base(options) { }

        public override async Task<bool> DataInit(object allModules, bool isSpa)
        {
            var state = await base.DataInit(allModules, isSpa);
            bool empty = false;
            try
            {
                empty = !Set<FrameworkUser>().Any() && !Set<FrameworkUserRole>().Any();
            }
            catch
            {
                // ignored
            }

            if (state || empty)
            {
                //when state is true, means it's the first time EF create database, do data init here
                //当state是true的时候，表示这是第一次创建数据库，可以在这里进行数据初始化
                var user = new FrameworkUser
                {
                    ITCode = "admin",
                    Password = Utils.GetMD5String("000000"),
                    IsValid = true,
                    Name = "Admin",
                                        
                };

                var serous = new FrameworkUserRole
                {
                    UserCode = user.ITCode,
                    RoleCode = "001"
                };
                var adminMenus = Set<FrameworkMenu>().Where(x => x.Url != null && x.Url.StartsWith("/api") == false).ToList();
                foreach (var item in adminMenus)
                {
                    item.Url = "/_admin" + item.Url;
                }
                await SaveChangesAsync();
                Set<FrameworkUser>().Add(user);
                Set<FrameworkUserRole>().Add(serous);
                await SaveChangesAsync();
                try{
                    Dictionary<string, List<object>> data = new Dictionary<string, List<object>>();
                }
                catch
                {
                    // ignored
                }
            }
            return state;
        }

        private void SetTestData(Type modelType, Dictionary<string, List<object>> data, int count = 100)
        {
            if (modelType.FullName != null && data.ContainsKey(modelType.FullName) && data[modelType.FullName].Count >= count)
            {
                return;
            }
            using var dc = this.CreateNew();
            Random r = new Random();
            data[modelType.FullName] = new List<object>();
            int retry = 0;
            List<string> ids = new List<string>();
            for (int i = 0; i < count; i++)
            {
                var modelProps = modelType.GetRandomValuesForTestData();
                var newObj = modelType.GetConstructor(Type.EmptyTypes).Invoke(null);
                var idValue = modelProps.Where(x => x.Key == "ID").Select(x=>x.Value).SingleOrDefault();
                if (idValue != null )
                {
                    if (ids.Contains(idValue.ToLower()) == false)
                    {
                        ids.Add(idValue.ToLower());
                    }
                    else
                    {
                        retry++;
                        i--;
                        if (retry > count)
                        {
                            break;
                        }
                        continue;
                    }
                }
                foreach (var pro in modelProps)
                {
                    if (pro.Value == "$fk$")
                    {
                        var fkType = modelType.GetSingleProperty(pro.Key[0..^2])?.PropertyType;
                        if (fkType != modelType && typeof(TopBasePoco).IsAssignableFrom(fkType)==true)
                        {
                            try
                            {
                                SetTestData(fkType, data, count);
                                newObj.SetPropertyValue(pro.Key, (data[fkType.FullName][r.Next(0, data[fkType.FullName].Count)] as TopBasePoco).GetID());
                            }
                            catch
                            {
                                // ignored
                            }
                        }
                    }
                    else
                    {
                        var v = pro.Value;
                        if (v.StartsWith("\""))
                        {
                            v = v[1..];
                        }
                        if (v.EndsWith("\""))
                        {
                            v = v[..^1];
                        }
                        newObj.SetPropertyValue(pro.Key, v);
                    }
                }
                if(modelType == typeof(FileAttachment))
                {
                    newObj.SetPropertyValue("Path", "./wwwroot/logo.png");
                    newObj.SetPropertyValue("SaveMode", "local");
                    newObj.SetPropertyValue("Length", 16728);
                }
                if (typeof(IBasePoco).IsAssignableFrom(modelType))
                {
                    newObj.SetPropertyValue("CreateTime", DateTime.Now);
                    newObj.SetPropertyValue("CreateBy", "admin");
                }
                if (typeof(IPersistPoco).IsAssignableFrom(modelType))
                {
                    newObj.SetPropertyValue("IsValid",true);
                }
                try
                {
                    (dc as DbContext).Add(newObj);
                    data[modelType.FullName].Add(newObj);
                }
                catch
                {
                    retry++;
                    i--;
                    if(retry > count)
                    {
                        break;
                    }
                }
            }
            try
            {
                dc.SaveChanges();
            }
            catch
            {
                // ignored
            }
        }

        /// <summary>
        /// 格式化decimal数据
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            var myDecimalConverter = new ValueConverter<decimal, decimal>(
                v => v,
                v => Convert.ToDecimal(Convert.ToDouble(v)));
            foreach (var entityType in modelBuilder.Model.GetEntityTypes())
            {
                foreach (var property in entityType.GetProperties())
                {
                    if (property.ClrType.Name == "Decimal")
                    {
                        property.SetValueConverter(myDecimalConverter);
                    }
                }
            }
            base.OnModelCreating(modelBuilder);
        }
    }

    /// <summary>
    /// DesignTimeFactory for EF Migration, use your full connection string,
    /// EF will find this class and use the connection defined here to run Add-Migration and Update-Database
    /// </summary>
    public class DataContextFactory : IDesignTimeDbContextFactory<DataContext>
    {
        public DataContext CreateDbContext(string[] args)
        {
            return new DataContext("server=172.16.99.15;port=3306;user=self-service;password=BrasTpdLkxmTwhrF;database=self-service;", DBTypeEnum.MySql);
        }
    }
}